home *** CD-ROM | disk | FTP | other *** search
- /* allocate pointers of fixed size
- 94/01/20 aih - when a pointer is disposed of all blocks that belong to
- that pointer are removed from the free list
- 94/01/17 aih - added list of pointers allocated from system, so can
- return free'd pointers to the system
- 94/01/07 aih - changed allocated block header to point to the free list
- instead of the block size, which makes disposal more efficient
- and the size can still be derived from the free list's block
- sizes.
- - improved data validation
- 94/01/02 aih - created */
-
- #include "LLPtrLib.h"
- #include "MemoryLib.h"
- #include "PointerFixedLib.h"
-
- /* Maximum size of pointers to allocate from system. Some memory allocators
- are optimized for pointer sizes that are powers of two, and may round
- up memory requests to the nearest power of two. Therefore, it's a good
- idea (though not required) to use a power of two as the pointer size. */
- #define MAXPTR (4096)
-
- /*----------------------------------------------------------------------------*/
- /* fixed-sized pointer allocation routines */
- /*----------------------------------------------------------------------------*/
-
- typedef struct FreeListStruct FreeListType;
-
- /* Header of a pointer allocated from the system, used so we can return
- the pointers to the system's memory pool. A reference count keeps track
- of how many blocks are allocated from the pointer. When the reference
- count reaches zero the pointer is disposed of. This means that allocating
- a small number of blocks and then soon after disposing of them would
- be fairly inefficient, since the pointer would be allocated and then
- disposed of; it would be better to allocate many blocks and then
- dispose of them altogether. */
- typedef struct {
- FreeListType *freelist; /* free list that owns this pointer */
- short refcnt; /* number of blocks allocated from this pointer */
- short nblocks; /* number of blocks this pointer can contain (for debugging) */
- short blksz; /* size of blocks allocated from this pointer (for debugging) */
- /* ... remainder of pointer divided into blocks ... */
- } SystemPointerType;
-
- /* Header of a block allocated with this library. By having the block header
- of an allocated block point to the pointer from which the block was
- allocated we can quickly insert the block back into the free list when
- the block is disposed of. Disposal of the pointer when the reference
- count reaches zero is also made reasonably efficient, since we have direct
- access to the pointer. */
- typedef struct {
- SystemPointerType *sysptr; /* pointer from which block was allocated */
- /* ... remainder of block contains data for use by application ... */
- } BlockAllocType;
-
- /* Header of a free block allocated with this library. The free block header
- is 8 bytes, while the allocated block header is only 4 bytes. This means
- that the overhead of a block allocated with this library is only 4 bytes,
- though the minimum size of blocks is 8 bytes since they must be at least
- large enough to contain the free block header (smaller blocks can still
- be allocated by the application, but they'll be rounded up to 8 bytes).
- We maintain a linked list of free blocks. When a block is allocated,
- it is removed from the free list and its header is converted to the
- header of an allocated block (see above). When a block is freed it is
- inserted into the free list and its header is converted to the header
- of a free block. By maintaining access to the pointer from which the block
- was allocated we can quickly decrement the reference count of the pointer,
- and when the reference count reaches zero we can dispose of the pointer. */
- typedef struct {
- LLType next; /* next free block */
- SystemPointerType *sysptr; /* pointer from which block was allocated */
- /* ... remainder of block is reserved for future allocation ... */
- } BlockFreeType;
-
- /* For calculating the minimum size of a block, we need the maximum of
- the size of an allocated block header and a free block header. This
- is easily accomplished by placing the structures into a union, with
- the property
- sizeof(BlockType) == max(sizeof(BlockAllocType), sizeof(BlockFreeType))
- We only use this union for calculating a block's size. */
- typedef union {
- BlockAllocType alloc;
- BlockFreeType free;
- } BlockType;
-
- /* List of free blocks. A separate list is maintained for each unique
- block size. */
- struct FreeListStruct {
- LLType next; /* next free list */
- short blksz; /* size of blocks */
- BlockFreeType *free; /* first free block */
- };
-
- /* true if a valid pointer allocated from the system */
- static Boolean SystemPointerValid(SystemPointerType *sysptr)
- {
- if (! PtrValidSize(sysptr, sizeof(SystemPointerType))) return(false);
- if (sysptr->nblocks < 0) return(false);
- if (sysptr->blksz < sizeof(BlockType)) return(false);
- if (sysptr->blksz != sysptr->freelist->blksz) return(false);
- if (sysptr->refcnt < 0 || sysptr->nblocks < sysptr->refcnt) return(false);
- if (PtrSize(sysptr) != sysptr->nblocks * sysptr->blksz + sizeof(SystemPointerType)) return(false);
- if (sysptr->nblocks * sysptr->blksz + sizeof(SystemPointerType) > MAXPTR) return(false);
- return(true);
- }
-
- /* true if a valid free list */
- static Boolean FreeListValid(FreeListType *list)
- {
- if (! PtrValidSize(list, sizeof(FreeListType))) return(false);
- if (list->blksz < 0 || MAXPTR < list->blksz) return(false);
- return(true);
- }
-
- /* find free list with items of specified size, create new list if needed */
- static FreeListType *FreeListFind(size_t blksz)
- {
- static FreeListType *free_lists;
- register FreeListType *list, *prev;
-
- /* find free list */
- prev = NULL;
- for (list = free_lists; list; list = LLPNext(list)) {
- if (list->blksz == blksz)
- break;
- prev = list;
- }
- if (! list) {
- /* create new list */
- free_lists = LLPInsert(free_lists, PtrBeginClear(sizeof(FreeListType)));
- free_lists->blksz = blksz;
- }
- else if (list != free_lists) {
- /* move found list to front */
- free_lists = LLPInsert(LLPDeleteFast(free_lists, list, prev), list);
- }
- ensure(FreeListValid(free_lists) && free_lists->blksz == blksz);
- return(free_lists);
- }
-
- /* allign and adjust size to be a valid block size, including the block header */
- static size_t AdjustSize(size_t size)
- {
- require(0 < size);
- require(size <= MAXPTR - sizeof(BlockType) - sizeof(SystemPointerType) - 1);
- if ((size & 0x01) != 0)
- size++;
- size += sizeof(BlockAllocType);
- if (size < sizeof(BlockType))
- size = sizeof(BlockType);
- ensure(sizeof(BlockType) <= size && size <= MAXPTR - sizeof(SystemPointerType));
- return(size);
- }
-
- /* true if a valid fixed-size pointer */
- Boolean PtrFixedValid(void *p)
- {
- BlockAllocType *blockalloc;
-
- if (! MemValid(p)) return(false);
- blockalloc = &((BlockAllocType *) p)[-1];
- if (! SystemPointerValid(blockalloc->sysptr)) return(false);
- if (blockalloc->sysptr->refcnt == 0) return(false);
- return(true);
- }
-
- /* True if a valid pointer of 'size' bytes. This differs from PtrValidSize,
- which only requires that a pointer be at least 'size' bytes long. The
- difference is due to the fixed size of pointers allocated wtih this
- library. */
- Boolean PtrFixedValidSize(void *p, size_t size)
- {
- /* check pointer */
- if (! PtrFixedValid(p)) return(false);
- if (((BlockAllocType *) p)[-1].sysptr->blksz != AdjustSize(size)) return(false);
- return(true);
- }
-
- /* Allocate a fixed-size pointer. The pointer is word-alligned. */
- void *PtrFixedBegin(size_t blksz)
- {
- FreeListType *list; /* free list from which to allocate a block */
- BlockFreeType *blockfree; /* block header for free block */
- BlockAllocType *blockalloc;/* block header for allocated block */
-
- /* get free list for blocks of requested size */
- blksz = AdjustSize(blksz);
- list = FreeListFind(blksz);
-
- /* allocate new pointer if no free blocks of requested size */
- if (! list->free) {
- SystemPointerType *sysptr; /* pointer allocated from system */
- short nblocks; /* number of blocks to allocate */
- short nbytes; /* number of bytes to allocate */
- char *p; /* pointer allocated from system */
-
- /* Allocate a pointer from the system to contain as many items of
- the requested size as will fit in MAXPTR bytes. By calling
- StripAddress here we can safely do address arithmetic on all
- pointers without having to call StripAddress anywhere else. */
- nblocks = (MAXPTR - sizeof(SystemPointerType)) / blksz;
- nbytes = nblocks * blksz + sizeof(SystemPointerType);
- p = StripAddress(PtrBegin(nbytes));
- sysptr = (SystemPointerType *) p;
- memclr(sysptr, sizeof(SystemPointerType));
-
- /* Initialize system pointer's header. For debugging, we keep track of
- the size of blocks allocated from this pointer and of how many blocks
- can be allocated from this pointer. */
- sysptr->freelist = list;
- sysptr->nblocks = nblocks;
- sysptr->blksz = blksz;
-
- /* divide pointer into blocks and join them into a linked list */
- p += sizeof(SystemPointerType);
- while (nblocks-- > 0) {
- list->free = LLPInsert(list->free, p);
- ((BlockFreeType *) p)->sysptr = sysptr;
- p += blksz;
- }
- }
-
- /* remove a block from the free list */
- blockfree = list->free;
- list->free = LLPDelete(list->free, blockfree);
-
- /* Set block header for an allocated block. By having the block header
- point to the pointer from which the block was allocated we can quickly
- dispose of the block. */
- blockalloc = (BlockAllocType *) blockfree;
- blockalloc->sysptr = blockfree->sysptr;
- blockfree = NULL;
-
- /* increment reference count for pointer */
- check(blockalloc->sysptr->refcnt < blockalloc->sysptr->nblocks);
- blockalloc->sysptr->refcnt++;
-
- ensure(PtrFixedValidSize(blockalloc + 1, blksz - sizeof(BlockAllocType)));
- return(blockalloc + 1);
- }
-
- /* dispose of a fixed-size pointer */
- void PtrFixedEnd(void *p)
- {
- FreeListType *list; /* free list from which block was allocated */
- SystemPointerType *sysptr; /* pointer from which block was allocated */
- BlockFreeType *blockfree; /* block header for free block */
- BlockAllocType *blockalloc; /* block header for allocated block */
-
- require(! p || PtrFixedValid(p));
- if (p) {
-
- /* convert block to a free block */
- blockalloc = (BlockAllocType *) p - 1;
- p = NULL;
- sysptr = blockalloc->sysptr;
- list = sysptr->freelist;
- blockfree = (BlockFreeType *) blockalloc;
- blockalloc = NULL;
-
- /* insert block into its free list */
- list->free = LLPInsert(list->free, blockfree);
- blockfree->sysptr = sysptr;
-
- /* decrement reference count of pointer containing block; dispose
- of pointer when reference count reaches zero */
- check(sysptr->refcnt > 0);
- if (--sysptr->refcnt == 0) {
- BlockFreeType *prev, *next;
-
- /* remove all blocks that belong to this pointer from the free list */
- /* program_note: this may make block disposal inefficient if there
- are many blocks (e.g., in the thousands or tens of thousands) */
- prev = NULL;
- blockfree = list->free;
- while (blockfree) {
- next = LLPNext(blockfree);
- if (blockfree->sysptr == sysptr)
- list->free = LLPDeleteFast(list->free, blockfree, prev);
- else
- prev = blockfree;
- blockfree = next;
- }
-
- /* dispose of pointer */
- PtrEnd(sysptr);
- }
- }
- ensure(! PtrFixedValid(p));
- }
-